﻿
using Microsoft.EntityFrameworkCore;
using PoemGame.Domain.GameAggregate;
using PoemGame.Domain.PlayerAggregate;
using PoemGame.Domain.Seedwork;
using PoemGame.Events.Shared;
using PoemGame.UOW;

namespace PoemGame.Repository.EF
{
    public class PoemGameDbContext:DbContext,IUnitOfWork
    {
        public DbSet<Player> Players { get; set; }

        public DbSet<Game> Games { get; set; }

        private readonly ILocalEventBus _localeventBus = default!;
        private readonly IOutBoundEventBus _remoteeventBus = default!;

        public PoemGameDbContext(DbContextOptions<PoemGameDbContext> options) : base(options)
        {
            
        }

        /// <summary>
        /// 带有事件发布的构造函数
        /// </summary>
        /// <param name="options"></param>
        /// <param name="localeventBus"></param>
        /// <param name="remoteEventBus"></param>
        public PoemGameDbContext(DbContextOptions<PoemGameDbContext> options
            , ILocalEventBus localeventBus, IOutBoundEventBus remoteEventBus) : base(options)
        {
            _localeventBus = localeventBus;
            _remoteeventBus = remoteEventBus;
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.ApplyConfiguration(new PlayerEntityTypeConfiguration());
            modelBuilder.ApplyConfiguration(new GameEntityTypeConfiguration());
        }

        public async override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
        {
            var modifiedChanges = 
                ChangeTracker.Entries()
                .Where(x => x.State == EntityState.Modified || x.State == EntityState.Added).ToList();

            var localEvents = new List<EventRecordLocal>();
            var remoteEvents = new List<EventRecordOutBound>();

            foreach (var change in modifiedChanges)
            {
                var entity = change.Entity as AggregateRoot;
                if (entity != null)
                {
                    foreach (var e in entity.GetLocalDomainEvents())
                    {
                        localEvents.Add(e);
                    }
                    entity.ClearLocalDomainEvents();
                    foreach (var e in entity.GetOutBoundDomainEvents())
                    {
                        remoteEvents.Add(e);
                    }
                    entity.ClearOutBoundDomainEvents();
                }
            }
            if (_localeventBus != null)
            {
                foreach (var e in localEvents)
                {
                    await _localeventBus.PublishAsync(e.EventData);
                }
            }

            var res=await base.SaveChangesAsync();

            if (_remoteeventBus != null)
            {
                foreach (var e in remoteEvents)
                {
                    await _remoteeventBus.PublishAsync(e.EventData);
                }
            }

            return res;
        }

    }
}
